package com.stottlerhenke.simbionic.engine.parser;
import java.util.*;
import com.stottlerhenke.simbionic.engine.core.*;
import com.stottlerhenke.simbionic.engine.debug.EEventType;
import com.stottlerhenke.simbionic.api.*;
import com.stottlerhenke.simbionic.engine.*;
import com.stottlerhenke.simbionic.common.SIM_Constants;
import com.stottlerhenke.simbionic.common.SB_Logger;
import com.stottlerhenke.simbionic.common.debug.*;


public class SB_ExpFunction extends SB_Expression {
  protected ArrayList _args = new ArrayList();
  private SB_Function _fun;
  public SB_ExpFunction(SB_Function fun) {
    super();
    _fun = fun;
  }
  public void AddArgument(SB_Expression arg){
    _args.add(arg);
  }
  public EExpressionType GetType(){
    return EExpressionType.kFunction;
  }
  public String PrintTree() throws SB_Exception{
    return new String("function(" + _fun.toString() + ")");
  }

  /**
   * Replaces all instances of SB_Method in the expression tree that
   * match the name of newMethod.
   * @param newMethod the method with which to replace existing methods
   */
  public void replaceFunction(SB_Function newMethod)
  {
      if (_fun.GetName().equals(newMethod.GetName()))
          _fun = newMethod;
      
      for (int i=0; i<_args.size(); ++i)
      {
          ((SB_Expression)_args.get(i)).replaceFunction(newMethod);
      }
  }
  
  public SB_Variable Evaluate(SB_Entity entity, SB_ExecutionFrame contextFrame) throws SB_Exception
  {
    ArrayList args = new ArrayList();
    SB_Expression expIt;
    for(int i = 0; i < _args.size(); i++){
      expIt =(SB_Expression) _args.get(i);
      args.add(expIt.Evaluate(entity, contextFrame));
    }

    //Go through the arguments and pre-save arrays
    for( int x = 0; x < _fun.GetParams().GetNumParams(); x++ )
    {
      if( _fun.GetParams().GetParam(x).IsOut() )
      {
        SB_Variable tempVariable = (SB_Variable) args.get(x);
        if( tempVariable.GetType() == SB_VarType.kArray )
        {
          SB_ExpVariable tempActual = (SB_ExpVariable) _args.get(x);
          contextFrame.SetVariable( tempActual.GetName(), tempVariable);
        }
      }
    }

    /* Deprecated
    if(SB_Function.IsReserved(_fun.GetId()))
    {
      if (SIM_Constants.DEBUG_INFO_ON)
        contextFrame.GetLogger().log("[" + entity.toString() + ",STK " + contextFrame.GetStackDepth()
        + "] PRED:\tinvoking " + _fun.toString(),SB_Logger.PREDICATE);

      SB_Variable retVal = SB_Function.DoReservedFunction(_fun, entity, args, contextFrame);

      if (SIM_Constants.DEBUG_INFO_ON)
        contextFrame.GetLogger().log("[" + entity.toString() + ",STK " + contextFrame.GetStackDepth()
	+ "] PRED:\tcompleted",SB_Logger.PREDICATE);

      //Now setup values for out variables
      for( int x = 0; x < _fun.GetParams().GetNumParams(); x++ )
      {
        if( _fun.GetParams().GetParam(x).IsOut() )
        {
          SB_Variable tempVariable = (SB_Variable) args.get(x);
          SB_ExpVariable tempActual = (SB_ExpVariable) _args.get(x);
          contextFrame.SetVariable( tempActual.GetName(), tempVariable);
        }
      }

      return retVal;
    }
   */
    
    ArrayList params = new ArrayList();
    SB_SimInterface.ConvertVariables(args, params);
    long id = SB_SimInterface.TranslateId(entity.GetId());

    if( SIM_Constants.AI_DEBUGGER )
    {
      DMFieldMap fields = new DMFieldMap();
      fields.ADD_ID_FIELD("entity", entity.GetId()._id );
      fields.ADD_STR_FIELD("funcName", _fun.GetName() );
      contextFrame.GetDebugger().RecordEvent(EEventType.kEVT_FUNCTION_INVOKED, fields);
    }

    if (SIM_Constants.DEBUG_INFO_ON)
      contextFrame.GetLogger().log("[" + entity.toString() + ",STK " + contextFrame.GetStackDepth()
      + "] PRED:\tinvoking " + _fun.toString(),SB_Logger.PREDICATE);

    //Caught for engine runtime exception handling
    SB_Param retVal = null;
    
    try 
		{
    	retVal = _fun.GetInterface().doPredicate(_fun.GetId(), id, params);
		}
    catch(SB_Exception ex)
		{
    	//Log the error
        if(SIM_Constants.DEBUG_INFO_ON)
        {
            contextFrame.GetLogger().log("[" + entity.toString() + ",STK " + contextFrame.GetStackDepth()
                                            + "] PRED:\tcaused an exception",SB_Logger.ERROR);
            contextFrame.GetLogger().log(ex.toString(), SB_Logger.ERROR);
        }
    	
    	SB_EntityData state = entity.GetState();
  		state.GetExecStack().HandleException(ex, contextFrame);
  		return null;
		}
    
    if (SIM_Constants.DEBUG_INFO_ON)
      contextFrame.GetLogger().log("[" + entity.toString() + ",STK " + contextFrame.GetStackDepth()
      + "] PRED:\tcompleted",SB_Logger.PREDICATE);

    _fun.GetParams().UnpackArgs(contextFrame, params, _args);
    return SB_SimInterface.ConvertParamAuto(retVal, contextFrame.GetBook());
  }
}